package ua.pp.bizon.yandex.api.impl;

import ua.pp.bizon.yandex.api.IJobExecutor;
import ua.pp.bizon.yandex.api.ILogin;
import ua.pp.bizon.yandex.api.Job;
import ua.pp.bizon.yandex.api.JobInfo;
import ua.pp.bizon.yandex.api.ObjectLookup;
import ua.pp.bizon.yandex.api.WindowManager;

import java.util.Collections;
import java.util.LinkedList;
import java.util.List;

import javax.management.Notification;
import javax.management.NotificationListener;
import javax.swing.SwingUtilities;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class JobExecutor implements Runnable, NotificationListener, IJobExecutor {

	public JobExecutor() {
		init();
	}

	private List<Job> jobs = Collections.synchronizedList(new LinkedList<Job>());
	private static final Log log = LogFactory.getLog(JobExecutor.class);
	private Job current = null;

	@Override
	public void exec(Job job) {
		log.trace("job adding: " + job);
		jobs.add(job);
		log.debug("job added: " + job);
		synchronized (this) {
			notify();
		}
		log.trace("job notifyed: " + job);
	}

	private void init() {
		Thread t = new Thread(this, "Job executor");
		// t.setDaemon(true);
		t.start();
		log.debug("executor has being started");
	}

	@Override
	public void run() {
		for (;;) {
			synchronized (this) {
				if (jobs.isEmpty()) {
					try {
						log.debug("executor has being sleeped");
						wait();
						log.debug("executor has being wake up");
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
			log.trace("executor has being started execution");
			synchronized (this) {
				current = jobs.remove(0);
			}
			log.trace("current job = " + current);
			try {
				current.run();
			} catch (Throwable e) {
				LogFactory.getLog(getClass()).error(e.getMessage(), e);
			}
			log.trace("job " + current + " has being finished");
			synchronized (this) {
				current = null;
			}
		}
	}

	@Override
	public void execInSwing(Job job) {
		SwingUtilities.invokeLater(job);
	}

	@Override
	public void execInSwing(Job job, boolean wait) {
		if (wait) {
			try {
				SwingUtilities.invokeAndWait(job);
			} catch (Exception e) {
				log.error(e.getMessage(), e);
			}
		} else {
			execInSwing(job);
		}
	}

	@Override
	public NotificationListener listener() {
		return this;
	}

	@Override
	public void exec(final Element e) {
		switch (e.getActivity()) {
		case START_GUI:
			execInSwing(new Job() {

				@Override
				public void run() {
					if (!ObjectLookup.lookup(WindowManager.class).mainFrame().isVisible()) {
						ObjectLookup.lookup(WindowManager.class).mainFrame().setVisible(true);
					}
				}
			});
			break;
		case OPEN_GUI:
			execInSwing(new Job() {
				@Override
				public void run() {
					if (!ObjectLookup.lookup(WindowManager.class).mainFrame().isVisible()) {
						ObjectLookup.lookup(WindowManager.class).mainFrame().setVisible(true);
					}
					ObjectLookup.lookup(WindowManager.class).mainFrame().requestFocus();
				}
			}, true);
			break;
		case RELOGIN:
			execInSwing(new Job() {
				@Override
				public void run() {
					ILogin frame = ObjectLookup.lookup(WindowManager.class).login(false);
					if (e.getProperties() != null && e.getProperties().containsKey(Element.PROP_LOGIN)) {
						frame.setLogin(e.getProperties().get(Element.PROP_LOGIN));
					}
				}
			});
			break;
		case UPLOAD_FOLDER:
			exec(new UploadFoldersJob(e.getProperties().get(Element.PROP_FROM), e.getProperties().get(Element.PROP_TO), this));
			break;
		default:
			log.error(e.getActivity() + " is not implemented");
			throw new RuntimeException(e.getActivity() + " is not implemented");
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * ua.pp.bizon.yandex.api.IJobEcecutor#handleNotification(javax.management
	 * .Notification, java.lang.Object)
	 */
	@Override
	public void handleNotification(Notification notification, Object handback) {
		log.debug("executor get notify " + notification);
		Object notify = notification.getSource();
		if (notify instanceof Element) {
			final Element e = (Element) notify;
			exec(e);
		}

	}

	public List<Element> getStateInfo() {
		List<Element> elements = new LinkedList<Element>();
		synchronized (this) {
			log.trace("getStateInfo");
			List<Job> jobsCopy = new LinkedList<Job>(jobs);
			log.trace("getStateInfo jobs count = " + jobsCopy.size());
			for (Job j : jobsCopy) {
				elements.add(getElement(j));
			}
			log.trace("getStateInfo current = " + current);
			if (current != null) {
				elements.add(getElement(current));
			}
		}
		log.trace("getStateInfo count = " + elements.size());
		return elements;
	}

	private Element getElement(Job j) {
		if (j instanceof JobInfo) {
			return ((JobInfo) j).getState();
		} else {
			return new Element();
		}
	}
}
